// (C) 2001-2021 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other
// software and tools, and its AMPP partner logic functions, and any output
// files from any of the foregoing (including device programming or simulation
// files), and any associated documentation or information are expressly subject
// to the terms and conditions of the Intel Program License Subscription
// Agreement, Intel FPGA IP License Agreement, or other applicable
// license agreement, including, without limitation, that your use is for the
// sole purpose of programming logic devices manufactured by Intel and sold by
// Intel or its authorized distributors.  Please refer to the applicable
// agreement for further details.

`include "espi_header.iv"

`timescale 1 ps / 1 ps

module espi_tx_shifter
  (
   // espi_condt_det
   input              stop_det,
   input              start_det,
   input              spiclk_cnt_done_hl,
   input              spiclk_cnt_done_lh,
   input              trigger_output,
   output logic       detect_tx_idle,
   output logic [7:0] tx_dataout_byte,
   
   // Broadly used
   output logic       tx_gen_command,
   output logic       tx_gen_header,
   output logic       tx_gen_cycletype,
   output logic       tx_gen_length_h,
   output logic       tx_gen_length_l,
   output logic       tx_gen_data,
   output logic [2:0] tx_data_array_cnt,
   output logic       tx_gen_status,
   output logic       tx_gen_crc,

   // espi_pc_channel
   input              pc_short_data_valid,
   input              pc_txfifo_avail,
   output logic       np_connected,

   // espi_resp_gen
   input              tx_with_cycletype,
   input [3:0]        resp_hdr_ptr,
   input [6:0]        resp_data_ptr,
   input [2:0]        resp_data_array,
   input [7:0]        resp_header,
   input [4:0]        resp_cycletype,
   input [2:0]        resp_cycletype_msb,
   input [7:0]        resp_data[4],
   input [7:0]        resp_status[2],

   // espi_rx_shifter
   input              rx_detect_crc,
   input              rx_detect_crc_end,

   // espi_cmd_det
   input              detect_iord_short,
   input              detect_iowr_short,
   input              detect_memrd_short,
   input              detect_memwr_short,
   input              detect_put_np_rxfifo,
   input              posted_cycle,

   // espi_resp_gen
   output logic       pec_mismatch_tx,

   // espi_slave
   input              error_condition1,
   input              error_condition2,
   output logic [1:0] tx_status_cnt,

   // espi_registers
   input [1:0]        io_mode,
   input [3:0]        max_wait_state,

   // Globals
   input              clk,
   input              ip_reset_n
   );

    localparam IDLE               = 3'b000;
    localparam TX_GEN_WAITSTATE   = 3'b001;
    localparam TX_GEN_CMD         = 3'b010;
    localparam TX_GEN_CYCLETYPE   = 3'b011;
    localparam TX_GEN_HDR         = 3'b100;
    localparam TX_GEN_DATA        = 3'b101;
    localparam TX_GEN_STATUS      = 3'b110;
    localparam TX_GEN_CRC         = 3'b111;
    localparam BYTE   = 8;
    localparam STATUS_BYTE        = 2'd1;  // 2 byte minus 1

    logic [7:0]       resp_command;
    logic [7:0]       resp_waitstate;
    logic [2:0]       resp_fsm_state;
    logic [2:0]       resp_fsm_nx_state;
    logic [6:0]       tx_data_ptr_cnt;
    logic [3:0]       tx_hdr_ptr_cnt;
    logic             load_tx_cnt;
    logic             decr_tx_cnt;
    logic             tx_hdr_ptr_cnt_done;
    logic             tx_status_cnt_done;
    logic             detect_txshiftstate;
    logic             tx_data_ptr_cnt_done;
    logic             tx_gen_waitstate;
    logic             trigger_output_nxt;
    logic             trigger_output_nxt2;
    logic             trigger_output_nxt3;
    logic [7:0]       max_wait_state_int;
    logic             ws_counter_done;
    logic [4:0]       ws_counter;
    logic             detect_short;
    logic             np_defer;
    logic             np_accept;
    logic             mono_io_mode;
    logic             dual_io_mode;
    logic             quad_io_mode;
    logic             tx_update_crc;
    logic [7:0]       tx_crc_data_in;
    logic             flush_crc;
    logic [7:0]       resp_crc;


    espi_crc_gen espi_tx_crc_gen_inst
      (
       .clk                (clk),
       .reset_n            (ip_reset_n),
       .crc_data_in        (tx_crc_data_in),
       .update_crc         (tx_update_crc),
       .verify_crc         (1'b0),
       .flush_crc          (flush_crc),
       .crc_code_out       (resp_crc),
       .pec_mismatch       (pec_mismatch_tx)
       );

    assign tx_update_crc  = (tx_gen_command || tx_gen_header || tx_gen_cycletype || tx_gen_data || 
                             tx_gen_status) && spiclk_cnt_done_lh;

    assign tx_crc_data_in = (tx_gen_command && spiclk_cnt_done_lh)   ? resp_command :
                            (tx_gen_cycletype && spiclk_cnt_done_lh) ? {resp_cycletype_msb, resp_cycletype} :
                            (tx_gen_header && spiclk_cnt_done_lh)    ? resp_header :
                            (tx_gen_data && spiclk_cnt_done_lh)      ? resp_data[tx_data_array_cnt] :
                            (tx_gen_status && spiclk_cnt_done_lh)    ? resp_status[tx_status_cnt] :
                            {8{1'b0}};
                            
    always @(posedge clk or negedge ip_reset_n) begin
        if (!ip_reset_n) begin
            flush_crc <= 1'b0;
        end
        else begin
            flush_crc <= stop_det;
        end
    end

    assign tx_dataout_byte = (tx_gen_waitstate) ? resp_waitstate :
                             (tx_gen_command) ? resp_command :
                             (tx_gen_cycletype) ? {resp_cycletype_msb, resp_cycletype} :
                             (tx_gen_header) ? resp_header :
                             (tx_gen_data) ? resp_data[tx_data_array_cnt] :
                             (tx_gen_status) ? resp_status[tx_status_cnt] :
                             (tx_gen_crc) ? resp_crc : {8{1'b0}};

    assign detect_tx_idle = (resp_fsm_state == IDLE);

    always @(posedge clk or negedge ip_reset_n) begin
        if (!ip_reset_n) begin
            np_connected <= 0;
            np_defer     <= 0;
        end
        else begin
            if (ws_counter_done) begin
                np_defer = ~np_accept;
            end
            if (stop_det) begin
                np_connected <= 0;
                np_defer     <= 0;
            end
            else if (tx_gen_command) begin
                np_connected <= ~np_defer & (detect_short | detect_put_np_rxfifo);
            end
        end
    end

    // Response phase steps
    always @(posedge clk or negedge ip_reset_n) begin
        if (!ip_reset_n) begin
            tx_gen_waitstate    <= 1'b0;
            tx_gen_command      <= 1'b0;
            tx_gen_cycletype    <= 1'b0;
            tx_gen_header       <= 1'b0;
            tx_gen_data         <= 1'b0;
            tx_gen_status       <= 1'b0;
            tx_gen_crc          <= 1'b0;
        end
        else begin
            case(resp_fsm_nx_state)
              IDLE : begin
                  tx_gen_waitstate    <= 1'b0;
                  tx_gen_command      <= 1'b0;
                  tx_gen_cycletype    <= 1'b0;
                  tx_gen_header       <= 1'b0;
                  tx_gen_data         <= 1'b0;
                  tx_gen_status       <= 1'b0;
                  tx_gen_crc          <= 1'b0;
              end

              TX_GEN_WAITSTATE : begin
                  tx_gen_waitstate    <= 1'b1;
                  tx_gen_command      <= 1'b0;
                  tx_gen_cycletype    <= 1'b0;
                  tx_gen_header       <= 1'b0;
                  tx_gen_data         <= 1'b0;
                  tx_gen_status       <= 1'b0;
                  tx_gen_crc          <= 1'b0;
              end

              TX_GEN_CMD : begin
                  tx_gen_waitstate    <= 1'b0;
                  tx_gen_command      <= 1'b1;
                  tx_gen_cycletype    <= 1'b0;
                  tx_gen_header       <= 1'b0;
                  tx_gen_data         <= 1'b0;
                  tx_gen_status       <= 1'b0;
                  tx_gen_crc          <= 1'b0;
              end

              TX_GEN_CYCLETYPE : begin
                  tx_gen_waitstate    <= 1'b0;
                  tx_gen_command      <= 1'b0;
                  tx_gen_cycletype    <= 1'b1;
                  tx_gen_header       <= 1'b1;
                  tx_gen_data         <= 1'b0;
                  tx_gen_status       <= 1'b0;
                  tx_gen_crc          <= 1'b0;
              end

              TX_GEN_HDR : begin
                  tx_gen_waitstate    <= 1'b0;
                  tx_gen_command      <= 1'b0;
                  tx_gen_cycletype    <= 1'b0;
                  tx_gen_header       <= 1'b1;
                  tx_gen_data         <= 1'b0;
                  tx_gen_status       <= 1'b0;
                  tx_gen_crc          <= 1'b0;
              end

              TX_GEN_DATA : begin
                  tx_gen_waitstate    <= 1'b0;
                  tx_gen_command      <= 1'b0;
                  tx_gen_cycletype    <= 1'b0;
                  tx_gen_header       <= 1'b0;
                  tx_gen_data         <= 1'b1;
                  tx_gen_status       <= 1'b0;
                  tx_gen_crc          <= 1'b0;
              end

              TX_GEN_STATUS : begin
                  tx_gen_waitstate    <= 1'b0;
                  tx_gen_command      <= 1'b0;
                  tx_gen_cycletype    <= 1'b0;
                  tx_gen_header       <= 1'b0;
                  tx_gen_data         <= 1'b0;
                  tx_gen_status       <= 1'b1;
                  tx_gen_crc          <= 1'b0;
              end

              TX_GEN_CRC : begin
                  tx_gen_waitstate    <= 1'b0;
                  tx_gen_command      <= 1'b0;
                  tx_gen_cycletype    <= 1'b0;
                  tx_gen_header       <= 1'b0;
                  tx_gen_data         <= 1'b0;
                  tx_gen_status       <= 1'b0;
                  tx_gen_crc          <= 1'b1;
              end

              default: begin
                  tx_gen_waitstate    <= 1'b0;
                  tx_gen_command      <= 1'b0;
                  tx_gen_cycletype    <= 1'b0;
                  tx_gen_header       <= 1'b0;
                  tx_gen_data         <= 1'b0;
                  tx_gen_status       <= 1'b0;
                  tx_gen_crc          <= 1'b0;
              end
            endcase // case (resp_fsm_nx_state)
        end // else: !if(!ip_reset_n)
    end // always @ (posedge clk or negedge ip_reset_n)

    assign resp_waitstate = `WAIT_STATE;

    // Set response
    always @(posedge clk or negedge ip_reset_n) begin
        if (!ip_reset_n) begin
            resp_command <= 8'h0;
        end
        else if (error_condition2) begin
            resp_command <= `FATAL_ERROR;
        end
        else if (detect_put_np_rxfifo) begin        // always defer non-posted read trans
            resp_command <= `DEFER;
        end
        else if (np_defer) begin
            // Read data is not available when response phase begins
            resp_command <= `DEFER;
        end
        else begin
            resp_command <= `ACCEPT;
        end
    end // always @ (posedge clk or negedge ip_reset_n)

    // Count out data array (double-word) bytes
    always @(posedge clk or negedge ip_reset_n) begin
        if (!ip_reset_n) begin
            tx_data_array_cnt <= 3'd0;
        end
        else begin
            if (tx_gen_data && spiclk_cnt_done_hl) begin
                if (tx_data_array_cnt < (resp_data_array - 3'd1)) begin        // count up until before the counter reach resp_data_array
                    tx_data_array_cnt <= tx_data_array_cnt + 3'd1;
                end
                else begin
                    tx_data_array_cnt <= 3'd0;
                end
            end
            else if (tx_gen_command) begin
                tx_data_array_cnt <= 3'd0;
            end
        end // else: !if(!ip_reset_n)
    end // always @ (posedge clk or negedge ip_reset_n)

    // Count out status bytes
    always @(posedge clk or negedge ip_reset_n) begin
        if (!ip_reset_n) begin
            tx_status_cnt <= 2'd0;
        end
        else begin
            if (tx_gen_status && spiclk_cnt_done_hl) begin
                if (tx_status_cnt < STATUS_BYTE) begin
                    tx_status_cnt <= tx_status_cnt + 2'd1;
                end
                else begin
                    tx_status_cnt <= 2'd0;
                end
            end
            else if (tx_gen_command) begin
                tx_status_cnt <= 2'd0;
            end
        end // else: !if(!ip_reset_n)
    end // always @ (posedge clk or negedge ip_reset_n)

    // Track the transport layer protocol for the response phase
    always @(posedge clk or negedge ip_reset_n) begin
        if (!ip_reset_n) begin
            resp_fsm_state <= IDLE;
        end
        else begin
            resp_fsm_state <= resp_fsm_nx_state;
        end
    end

    always @* begin
        case(resp_fsm_state)
          IDLE : begin
              if ((io_mode[1] ? rx_detect_crc_end : rx_detect_crc))
                resp_fsm_nx_state = TX_GEN_WAITSTATE;
              else
                resp_fsm_nx_state = IDLE;
          end

          TX_GEN_WAITSTATE : begin
              if (posted_cycle || (np_accept && spiclk_cnt_done_hl))
                resp_fsm_nx_state = TX_GEN_CMD;
              else if (ws_counter_done)
                resp_fsm_nx_state = TX_GEN_CMD;
              else
                resp_fsm_nx_state = TX_GEN_WAITSTATE;
          end // case: TX_GEN_WAITSTATE

          TX_GEN_CMD : begin
              if (spiclk_cnt_done_hl)
                if (error_condition2)
                  resp_fsm_nx_state = TX_GEN_STATUS;
                else if (np_defer)
                  resp_fsm_nx_state = TX_GEN_STATUS;
                else if (tx_with_cycletype)
                  resp_fsm_nx_state = TX_GEN_CYCLETYPE;
                else if (resp_hdr_ptr > 4'h0)
                  resp_fsm_nx_state = TX_GEN_HDR;
                else if (resp_data_ptr > 7'h0)
                  resp_fsm_nx_state = TX_GEN_DATA;
                else
                  resp_fsm_nx_state = TX_GEN_STATUS;
              else
                resp_fsm_nx_state = TX_GEN_CMD;
          end // case: TX_GEN_CMD

          TX_GEN_CYCLETYPE: begin
              if (spiclk_cnt_done_hl)
                resp_fsm_nx_state = TX_GEN_HDR;
              else
                resp_fsm_nx_state = TX_GEN_CYCLETYPE;
          end

          TX_GEN_HDR : begin
              if (spiclk_cnt_done_hl)
                if (tx_hdr_ptr_cnt_done)
                  if (resp_data_ptr > 7'h0)
                    resp_fsm_nx_state = TX_GEN_DATA;
                  else
                    resp_fsm_nx_state = TX_GEN_STATUS;
                else
                  resp_fsm_nx_state = TX_GEN_HDR;
              else
                resp_fsm_nx_state = TX_GEN_HDR;
          end // case: TX_GEN_HDR

          TX_GEN_DATA : begin
              if (spiclk_cnt_done_hl)
                if (tx_data_ptr_cnt_done)
                  resp_fsm_nx_state = TX_GEN_STATUS;
                else
                  resp_fsm_nx_state = TX_GEN_DATA;
              else
                resp_fsm_nx_state = TX_GEN_DATA;
          end // case: TX_GEN_DATA

          TX_GEN_STATUS : begin
              if (spiclk_cnt_done_hl)
                if (tx_status_cnt_done)
                  resp_fsm_nx_state = TX_GEN_CRC;
                else
                  resp_fsm_nx_state = TX_GEN_STATUS;
              else
                resp_fsm_nx_state = TX_GEN_STATUS;
          end // case: TX_GEN_STATUS

          TX_GEN_CRC : begin
              if (spiclk_cnt_done_hl)
                resp_fsm_nx_state = IDLE;
              else
                resp_fsm_nx_state = TX_GEN_CRC;
          end

          default: resp_fsm_nx_state = 3'b000;
        endcase // case (resp_fsm_state)

        // These override everything else
        if (error_condition1) begin
            // No Response
            resp_fsm_nx_state = IDLE;
        end
        else if (stop_det) begin
            // Deassertion of CS_N always stops everything
            resp_fsm_nx_state = IDLE;
        end

    end // always @ *

    assign np_accept = (detect_short & pc_short_data_valid) |
                       (detect_put_np_rxfifo & pc_txfifo_avail);

    assign detect_short = (detect_iord_short | detect_iowr_short |
                           detect_memrd_short | detect_memwr_short);
    

    assign tx_data_ptr_cnt_done = (tx_data_ptr_cnt == (resp_data_ptr-7'h1));

    // Count out data bytes
    always @(posedge clk or negedge ip_reset_n) begin
        if (!ip_reset_n) begin
            tx_data_ptr_cnt <= 7'd0;
        end
        else begin
            if (tx_gen_data && spiclk_cnt_done_hl) begin
                if (tx_data_ptr_cnt < (resp_data_ptr - 7'd1)) begin
                    tx_data_ptr_cnt <= tx_data_ptr_cnt + 7'd1;
                end
                else begin
                    tx_data_ptr_cnt <= 7'd0;
                end
            end
            else if (tx_gen_command) begin
                tx_data_ptr_cnt <= 7'd0;
            end
        end // else: !if(!ip_reset_n)
    end // always @ (posedge clk or negedge ip_reset_n)

    assign tx_hdr_ptr_cnt_done = (tx_hdr_ptr_cnt == (resp_hdr_ptr-4'h1));

    // Count out header bytes
    always @(posedge clk or negedge ip_reset_n) begin
        if (!ip_reset_n) begin
            tx_hdr_ptr_cnt <= 4'd0;
        end
        else begin
            if (tx_gen_header && spiclk_cnt_done_hl) begin
                if (tx_hdr_ptr_cnt < (resp_hdr_ptr - 4'd1)) begin
                    tx_hdr_ptr_cnt <= tx_hdr_ptr_cnt + 4'd1;
                end
                else begin
                    tx_hdr_ptr_cnt <= 4'd0;
                end
            end
            else if (tx_gen_command) begin
                tx_hdr_ptr_cnt <= 4'd0;
            end
        end // else: !if(!ip_reset_n)
    end // always @ (posedge clk or negedge ip_reset_n)

    assign tx_status_cnt_done = (tx_status_cnt == STATUS_BYTE);
    assign tx_gen_length_l = (tx_gen_header && tx_hdr_ptr_cnt == 4'd2);
    assign tx_gen_length_h = (tx_gen_header && tx_hdr_ptr_cnt == 4'd1);

    assign ws_counter_done = (quad_io_mode && (max_wait_state_int == 0)) ? (spiclk_cnt_done_hl &
                                                                            trigger_output_nxt2) :
                             ((ws_counter == max_wait_state_int) & spiclk_cnt_done_hl &
                              tx_gen_waitstate);

    always @(posedge clk or negedge ip_reset_n) begin
        if (!ip_reset_n) begin
            ws_counter <= 5'h0;
        end
        else begin
            if (ws_counter_done || error_condition1 || posted_cycle) begin
                ws_counter <= 5'h0;
            end
            else if (spiclk_cnt_done_hl && ws_counter > 5'h0) begin
                ws_counter <= ws_counter + 5'h1;
            end
            else if (trigger_output_nxt2 && ~trigger_output_nxt3 &&
              (ws_counter < max_wait_state_int)) begin
                ws_counter <= 5'h1;
            end
        end // else: !if(!ip_reset_n)
    end // always @ (posedge clk or negedge ip_reset_n)

    always @(posedge clk or negedge ip_reset_n) begin
        if (!ip_reset_n) begin
            trigger_output_nxt  <= 1'b0;
            trigger_output_nxt2 <= 1'b0;
            trigger_output_nxt3 <= 1'b0;
        end
        else begin
            trigger_output_nxt  <= trigger_output;
            trigger_output_nxt2 <= trigger_output_nxt;
            trigger_output_nxt3 <= trigger_output_nxt2;
        end
    end // always @ (posedge clk or negedge ip_reset_n)
    
    // Decrement by 1 here so as not to have to do it everywhere else
    // Note that max_wait_state never results in a 0 byte-time
    assign max_wait_state_int = ({~|max_wait_state, max_wait_state} << io_mode) - 1'd1;

    assign mono_io_mode = io_mode == 2'b00;
    assign dual_io_mode = io_mode == 2'b01;
    assign quad_io_mode = io_mode == 2'b10;
endmodule // espi_tx_shifter
